﻿using System;
#if WINDOWS_PHONE
using StyleMVVM.Data.Collections;
#else
using System.Collections.Concurrent;
#endif
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StyleMVVM.Logging
{
	public sealed class Logger
	{
		private static readonly object lockObject = new object();
		private static volatile ILog logger;
		private static volatile ILogService logService;

		public static ILog GetLogger(string loggerName)
		{
			if (logService != null)
			{
				return logService.GetLogger(loggerName);
			}

			return logger;
		}

		public static void Debug(string message)
		{
			Debug(message, null, null);
		}

		public static void Debug(string message, string supplemental)
		{
			Debug(message, supplemental, null);
		}

		public static void Debug(string message, string supplemental, Exception exp)
		{
			if (logger == null)
			{
				CreateLogger();
			}

			logger.Log(
				new LogEntry
					{
						Message = message,
						Supplemental = supplemental,
						Exception = exp,
						Level = LogLevel.DEBUG
					});
		}

		public static void Info(string message)
		{
			Info(message, null, null);
		}

		public static void Info(string message, string supplemental)
		{
			Info(message, supplemental, null);
		}

		public static void Info(string message, string supplemental, Exception exp)
		{
			if (logger == null)
			{
				CreateLogger();
			}

			logger.Log(
				new LogEntry
					{
						Message = message,
						Supplemental = supplemental,
						Exception = exp,
						Level = LogLevel.INFO
					});
		}

		public static void Warn(string message)
		{
			Warn(message, null, null);
		}

		public static void Warn(string message, string supplemental)
		{
			Warn(message, supplemental, null);
		}

		public static void Warn(string message, string supplemental, Exception exp)
		{
			if (logger == null)
			{
				CreateLogger();
			}

			logger.Log(
				new LogEntry
					{
						Message = message,
						Supplemental = supplemental,
						Exception = exp,
						Level = LogLevel.WARN
					});
		}

		public static void Error(string message)
		{
			Error(message, null, null);
		}

		public static void Error(string message, string supplemental)
		{
			Error(message, supplemental, null);
		}

		public static void Error(string message, string supplemental, Exception exp)
		{
			if (logger == null)
			{
				CreateLogger();
			}

			logger.Log(
				new LogEntry
					{
						Message = message,
						Supplemental = supplemental,
						Exception = exp,
						Level = LogLevel.ERROR
					});
		}

		public static void Fatal(string message)
		{
			Fatal(message, null, null);
		}

		public static void Fatal(string message, string supplemental)
		{
			Fatal(message, supplemental, null);
		}

		public static void Fatal(string message, string supplemental, Exception exp)
		{
			if (logger == null)
			{
				CreateLogger();
			}

			logger.Log(
				new LogEntry
					{
						Message = message,
						Supplemental = supplemental,
						Exception = exp,
						Level = LogLevel.FATAL
					});
		}

		public static void Log(LogLevel logLevel, string message)
		{
			Log(logLevel, message, null, null);
		}

		public static void Log(LogLevel logLevel, string message, string supplemental)
		{
			Log(logLevel, message, supplemental, null);
		}

		public static void Log(LogLevel logLevel, string message, string supplemental, Exception exp)
		{
			if (logger == null)
			{
				CreateLogger();
			}

			logger.Log(
				new LogEntry
					{
						Message = message,
						Supplemental = supplemental,
						Exception = exp,
						Level = logLevel
					});
		}

		private static void CreateLogger()
		{
			lock (lockObject)
			{
				if (logger == null)
				{
					if (Bootstrapper.InstanceIsReady)
					{
						logService =
							Bootstrapper.Instance.Container.LocateByType(typeof(ILogService)) as ILogService;

						logger = logService.GetLogger(null);
					}
					else
					{
						logger = new HoldQueueLog(BootStrapperReady);
					}
				}
			}
		}

		private static async void BootStrapperReady(ConcurrentQueue<LogEntry> obj)
		{
			try
			{
				ILogService tempLogService =
					Bootstrapper.Instance.Container.LocateByType(typeof(ILogService)) as ILogService;

				var tempLogger = tempLogService.GetLogger(string.Empty);

				await tempLogger.Configure();

				LogEntry logEntry;

				while (obj.TryDequeue(out logEntry))
				{
					tempLogger.Log(logEntry);
				}

				HoldQueueLog holdingQueue = logger as HoldQueueLog;

				if (holdingQueue != null)
				{
					holdingQueue.RealLog = tempLogger;
				}

				logService = tempLogService;
				logger = tempLogger;

				while (obj.TryDequeue(out logEntry))
				{
					tempLogger.Log(logEntry);
				}

				logger.Flush();
			}
			catch (Exception)
			{
			}
		}
	}
}